One important feature of this codec is that does not convert QuickDraw GX pictures to QuickDraw PICT in the traditional sense of the word "convert." What this codec allows is the embedding of GX objects inside a PICT file format. The advantage of this is that it allows GX pictures to be viewed (but not edited) in any application that can open a PICT file. Although "embedding" is very useful, it is quite different from "conversion."
Strictly speaking, it is not possible "convert" QuickDraw GX pictures to QuickDraw PICTs without loss of information because GX has much greater functionality than traditional QuickDraw. There is no way to represent complex transfer modes, perspective, advances typography, etc. using the QuickDraw imaging model. By using this codec, you do not lose any of these features.
If you looking for information on how to use the codec, here is sample code. This file (which was accidentally omitted from the last SDK) called "DecompressShape.c" contains all the code you need to embed a GX shape into a PICT using the new codec. The important routine in this file is:
PicHandle DecompressShape (gxShape theShape, PicHandle proxie, Boolean forPrintingOnly, Boolean eraseBackground)
A proxie can also be embedded so that the PICT can be viewed on machines without GX. On the September 95 GX SDK, this source file will be added to the GX Libraries folder (it may be renamed however to give abetter indication of its function). Note that the "DecompressShape.c" technique is quite different from that used in the older "PicturesAndPICTLibrary.c" which embeds a GX shape into a PICT using picComments. We recommend you use DecompressShape because picComments have several weaknesses including:
/* File: DecompressShape.h Contains: graphics libraries - shape decompression Written by: Mike Reed Copyright: (c) 1995 by Apple Computer, Inc., all rights reserved. Writers: (jtd) John Daggett Change History (most recent first): <1> 9/14/95 jtd First checked in. */ #pragma once #ifndef decompressShapeIncludes #define decompressShapeIncludes #include <QuickDraw.h> #include <GXTypes.h> #ifdef __cplusplus extern "C" { #endif Handle CreateQDGXStream(gxShape source, PicHandle proxie, Boolean forPrintingOnly, Boolean eraseBackground); PicHandle DecompressShape(gxShape theShape, PicHandle proxie, Boolean forPrintingOnly, Boolean eraseBackground); PicHandle ShapeToPICT(gxShape source); void ShapeToScrap(gxShape source, Boolean addProxie, Boolean forPrintingOnly, Boolean eraseBackground); void DragAndDropShape(EventRecord* event, gxShape shape); #ifdef __cplusplus }; #endif #endif /* File: DecompressShape.c Contains: graphics libraries - shape decompression Written by: Mike Reed Copyright: (c) 1995 by Apple Computer, Inc., all rights reserved. Writers: (jtd) John Daggett Change History (most recent first): <2> 9/14/95 jtd replaced boolean with Boolean <1> 9/14/95 jtd First checked in. */ #include <Drag.h>#include <Gestalt.h>#include <ImageCompression.h>#include <Memory.h>#include <Scrap.h> #include <GXTypes.h>#include <GXMath.h>#include <GXGraphics.h>#include <GXEnvironment.h>#include "StorageLibrary.h" #include "DecompressShape.h" #define LONGALIGN(n) (((n) + 3) & ~3L) #define kAtomHeaderSize (sizeof(Size) + sizeof(OSType)) static void RectangleToRect(const gxRectangle* gxr, Rect* qdr) { qdr->left = FixedRound(gxr->left); qdr->top = FixedRound(gxr->top); qdr->right = FixedRound(gxr->right); qdr->bottom = FixedRound(gxr->bottom); } static long* AppendAtom(long stream[], Size size, OSType tag, const void* data) { #ifdef debugging if (size & 3) DebugStr("\patom size needs to be long aligned"); #endif *stream++ = size + kAtomHeaderSize; *stream++ = tag; BlockMove(data, (Ptr)stream, size); return (long*)((char*)stream + size); } /* * See the comment on DecompressShape for an explaination of the parameters. * This routine is used by both DecompressShape for embedding shapes in PICTs, * and AddQDGXRecorderFrame for making gx movies. */ Handle CreateQDGXStream(gxShape source, PicHandle proxie, Boolean forPrintingOnly, Boolean eraseBackground) { #define gxForPrintingOnlyAtom 'fpto' #define gxEraseBackgroundAtom 'erbg' long atomCount, shapeSize, proxieSize, dataSize, fontListSize, eraseSize; Handle dataHdl, shapeHdl; gxFlatFontList* fontList; gxTag fontListTag; #ifdef debugging GXIgnoreGraphicsWarning(tags_of_type_flst_removed); #endif shapeHdl = ShapeToHandleWithFlags(source, gxFontListFlatten | gxFontGlyphsFlatten | gxFontVariationsFlatten); #ifdef debugging GXPopGraphicsWarning(); #endif if (shapeHdl == nil) return nil; if (proxie) { atomCount = 2; proxieSize = LONGALIGN(GetHandleSize((Handle)proxie)); } else { atomCount = 1; proxieSize = 0; } shapeSize = LONGALIGN(GetHandleSize(shapeHdl)); if (forPrintingOnly) ++atomCount; if (eraseBackground) ++atomCount; fontListSize = 0; fontList = nil; GXIgnoreGraphicsWarning(count_out_of_range); if (GXGetShapeTags(source, gxFlatFontListItemTag, 1, 1, &fontListTag) > 0) { fontListSize = GXGetTag(fontListTag, nil, nil); if (fontListSize > 0) { fontList = (gxFlatFontList*)NewPtr(fontListSize); if (fontList != nil) { GXGetTag(fontListTag, nil, fontList); fontListSize = LONGALIGN(fontListSize); ++atomCount; } else fontListSize = 0; } } GXPopGraphicsWarning(); // count_out_of_range dataSize = atomCount * kAtomHeaderSize + shapeSize + proxieSize + fontListSize + sizeof(long); dataHdl = NewHandle(dataSize); if (dataHdl == nil) { DisposHandle(shapeHdl); if (fontList) DisposPtr((Ptr)fontList); return nil; } { long* p = (long*)*dataHdl; if (forPrintingOnly) p = AppendAtom(p, 0, gxForPrintingOnlyAtom, nil); if (eraseBackground) p = AppendAtom(p, 0, gxEraseBackgroundAtom, nil); if (proxie) p = AppendAtom(p, proxieSize, 'PICT', *proxie); if (fontList) p = AppendAtom(p, fontListSize, gxFlatFontListItemTag, fontList); p = AppendAtom(p, shapeSize, 'qdgx', *shapeHdl); *p++ = 0; // end of the atom-list DisposHandle(shapeHdl); if (fontList) DisposPtr((Ptr)fontList); } return dataHdl; } static void GetRidOfAnyQDShapeTags(gxShape shape) { gxShapeType shapeType = GXGetShapeType(shape); if (shapeType == gxPictureType) { long index, count; gxShape* subShapes; count = GXGetPicture(shape, nil, nil, nil, nil); if (count > 0) { subShapes = (gxShape*)NewPtr(count * sizeof(gxShape)); if (subShapes != nil) { GXGetPicture(shape, subShapes, nil, nil, nil); for (index = 0; index < count; index++) GetRidOfAnyQDShapeTags(subShapes[index]); DisposPtr((Ptr)subShapes); } } } else if (shapeType == gxRectangleType && GXGetShapeTags(shape, gxQuickDrawPictTag, 1, gxSelectToEnd, nil) > 0) GXSetShapeType(shape, gxPictureType); } /* * This guy returns a Quickdraw picture containing an embedded shape, and a proxie * of the shape, if proxie is not nil. This is called by ShapeToScrap and DragAndDropShape. * * theShape * the shape you want to embedd in a PICT * proxie * a PICT to be drawn if theShape cannot be drawn (optional but recommended) * forPrintingOnly * if TRUE, then the decompressor will always look for the proxie * and theShape will only be used when printing. Use this setting if * theShape might be too large or too slow when drawn from other apps. * * If FALSE, then the decompressor will draw theShape unless it * gets an error, in which case it will look for a proxie. * eraseBackground * if TRUE, the decompressor will always erase the background to WHITE * before drawing the shape. This is slower, but needed if the shape does not * fill its bounding rectangle. * * if FALSE, the decompressor will just draw the shape. Use this setting * if the shape entirely fills its bounding rectangle. * * The shape [and proxie] is embedded by constructing a stream of atoms. Each atom begins * with a size (long) and a type (OSType) and then the data for that type. After the last atom, * there is a trailing zero (long) to mark the end of the stream. For embedded shapes, the type * is 'qdgx', and for the proxie the type is 'PICT'. Note that the size fields are rounded up to * a multiple of 4. Finally, to alert QuickTime that the data is in this parsable form with a * possible PICT proxie, we add a 'prxy' extension to the ImageDescriptionHandle. * * Picture of this form will draw the embedded shape when an application calls DrawPicture * if GX is around, and if not, the proxie will be drawn. When printed, the shape or the proxie * will be printed. This is meant to replace the PicComment described in GX 1.0 for embedding * shapes in pictures. * * If you want to include a flatFontList tag, be sure that theShape is a picture, otherwise GX will * not return the tag after GXFlattenShape. The flatFontList tag makes certain printing conditions * more efficient (i.e. font downloading to postscript printers). * * Your shape must not contain a gxQuickDrawPictTag, meaning it contains embedded QD data, becuase * this will potentially crash when it tries to print. To fix that, DecompressShape looks for occurrances * of the tag, and converts them to real gx data by calling GXSetShapeType(shape, gxPictureType). */ PicHandle DecompressShape(gxShape theShape, PicHandle proxie, Boolean forPrintingOnly, Boolean eraseBackground) { #define kQuickTimeGestalt 'qtim' PicHandle thePicture; ImageDescriptionHandle descHdl; ImageDescriptionPtr descPtr; Handle dataHdl; long version; if (Gestalt(kQuickTimeGestalt, &version) != noErr) return nil; GetRidOfAnyQDShapeTags(theShape); /* * Move the shape's topLeft to 0,0 so that it draws neatly inside the picture frame. * Note that the qdgx movie library does not move the shape, since the shape may not * take up the whole frame. */ { gxRectangle bounds; GXGetShapeLocalBounds(theShape, &bounds); if (bounds.left || bounds.top) GXMoveShape(theShape, -bounds.left, -bounds.top); dataHdl = CreateQDGXStream(theShape, proxie, forPrintingOnly, eraseBackground); if (bounds.left || bounds.top) GXMoveShape(theShape, bounds.left, bounds.top); } if (dataHdl == nil) return nil; descHdl = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription)); if (descHdl) { Rect shortBounds; gxRectangle bounds; GXGetShapeLocalBounds(theShape, &bounds); RectangleToRect(&bounds, &shortBounds); OffsetRect(&shortBounds, -shortBounds.left, -shortBounds.top); // set the topLeft of the src to 0,0 thePicture = OpenPicture(&shortBounds); descPtr = *descHdl; descPtr->idSize = sizeof(ImageDescription); descPtr->cType = 'qdgx'; descPtr->vendor = 'appl'; descPtr->temporalQuality = codecLosslessQuality; descPtr->width = shortBounds.right; descPtr->height = shortBounds.bottom; descPtr->hRes = descPtr->vRes = ff(72); descPtr->dataSize = GetHandleSize(dataHdl); descPtr->frameCount = 1; descPtr->depth = 32; descPtr->clutID = -1; // If there is a PICT proxie, add an image extension to tell QuickTime, in case GX is not around. if (proxie) { Handle prxyVersionHdl = NewHandle(sizeof(long)); if (prxyVersionHdl != nil) { *(long*)*prxyVersionHdl = 0; // version number for 'prxy' extension SetImageDescriptionExtension(descHdl, prxyVersionHdl, 'prxy'); } } HLock(dataHdl); DecompressImage(*dataHdl, descHdl, ((CGrafPtr)qd.thePort)->portPixMap, &shortBounds, &shortBounds, srcCopy, nil); DisposeHandle((Handle)descHdl); ClosePicture(); } else thePicture = nil; DisposHandle(dataHdl); return thePicture; } /* * This guy returns a Quickdraw picture containing a 1-bit bitmap of the shape. * This is used by ShapeToScrap to create a proxie when calling DecompressShape. * If you want to make the proxie prettier (and larger), change the bitmap to 8-bit. * However, if you're using this in conjunction with DecompressShape to place a * gxShape on the clipboard, 1-bit should be enough, since the actual shape will be * drawn, rather than the proxie (unless forPrintingOnly is true). */ PicHandle ShapeToPICT(gxShape source) { gxRectangle bounds; gxShape bitShape; gxBitmap bitmap; PicHandle thePicture; Rect shortBounds; /* * GetShapeLocalBounds doesn't accurately report the bounds of a gxQuickDrawPictTag. * One option is to convert the tags to real GX pictures. */ GetRidOfAnyQDShapeTags(source); GXGetShapeLocalBounds(source, &bounds); RectangleToRect(&bounds, &shortBounds); OffsetRect(&shortBounds, -shortBounds.left, -shortBounds.top); bitmap.width = shortBounds.right; bitmap.height = shortBounds.bottom; bitmap.rowBytes = bitmap.width + 31 >> 5 << 2; bitmap.pixelSize = 1; bitmap.space = gxIndexedSpace; bitmap.set = nil; bitmap.profile = nil; bitmap.image = NewPtrClear(bitmap.rowBytes * bitmap.height); if (bitmap.image == nil) return nil; bitShape = GXNewBitmap(&bitmap, nil); if (bitShape != nil) { gxViewGroup group = GXNewViewGroup(); gxViewDevice device = GXNewViewDevice(group, bitShape); gxViewPort port = GXNewViewPort(group); gxTransform trans = GXCloneTransform(GXGetShapeTransform(source)); GXSetShapeAttributes(source, GXGetShapeAttributes(source) | gxMapTransformShape); GXMoveShape(source, -bounds.left, -bounds.top); GXSetViewPortDither(port, 4); GXSetShapeViewPorts(source, 1, &port); GXDrawShape(source); GXSetShapeTransform(source, trans); GXDisposeTransform(trans); GXDisposeViewGroup(group); /* this disposes the gxViewPort and gxViewDevice */ GXDisposeShape(bitShape); } { GrafPtr thePort; BitMap srcBits; GetPort(&thePort); srcBits.baseAddr = bitmap.image; srcBits.rowBytes = bitmap.rowBytes; srcBits.bounds = shortBounds; thePicture = OpenPicture(&shortBounds); CopyBits(&srcBits, &thePort->portBits, &shortBounds, &shortBounds, srcOr, nil); ClosePicture(); } DisposPtr((Ptr)bitmap.image); return thePicture; } /* * This guy puts a Quickdraw picture on the clipboard containing an embedded shape * and, if addProxie is true, a 1-bit bitmap of the shape. Call this in response to * the user choosing "Copy" or "Cut" from the Edit menu. See comment for DecompressShape * to explain forPrintingOnly. */ void ShapeToScrap(gxShape source, Boolean addProxie, Boolean forPrintingOnly, Boolean eraseBackground) { PicHandle picture, proxie; proxie = addProxie ? ShapeToPICT(source) : nil; picture = DecompressShape(source, proxie, forPrintingOnly, eraseBackground); if (proxie) KillPicture(proxie); if (picture) { HLock((Handle)picture); ZeroScrap(); PutScrap(GetHandleSize((Handle)picture), 'PICT', (Ptr)*picture); KillPicture(picture); } } /* * The ItemReference is the gxShape to be sent. The dragSendRefCon is ignored. */ static pascal OSErr LibrarySendDataProc(FlavorType theType, void *dragSendRefCon, ItemReference theItem, DragReference theDrag) { OSErr result = noErr; gxShape shape = (gxShape)theItem; switch (theType) { case 'qdgx': { Handle flat = ShapeToHandle(shape); if (flat) { HLock(flat); result = SetDragItemFlavorData(theDrag, theItem, 'qdgx', *flat, GetHandleSize(flat), 0); DisposHandle(flat); } break; } case 'PICT': { PicHandle proxie = ShapeToPICT(shape); PicHandle pict = DecompressShape(shape, proxie, false, true); if (proxie) KillPicture(proxie); if (pict) { HLock((Handle)pict); result = SetDragItemFlavorData(theDrag, theItem, 'PICT', (Ptr)*pict, GetHandleSize((Handle)pict), 0); KillPicture(pict); } break; } default: result = badDragFlavorErr;
Main | Page One | What's New | Apple Computer, Inc. | Find It | Contact Us | Help